package com.ltedu.system.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ltedu.common.contants.Constants;
import com.ltedu.common.enums.ErrorCodeEnum;
import com.ltedu.common.exception.BaseException;
import com.ltedu.system.domain.SysMenu;
import com.ltedu.system.domain.SysMenuRole;
import com.ltedu.system.domain.dto.MenuNode;
import com.ltedu.system.domain.vo.MetaVO;
import com.ltedu.system.domain.vo.RouterVO;
import com.ltedu.system.domain.vo.SysMenuVO;
import com.ltedu.system.domain.vo.TreeSelect;
import com.ltedu.system.mapper.SysMenuMapper;
import com.ltedu.system.service.SysMenuRoleService;
import com.ltedu.system.service.SysMenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ltedu.system.service.SysRoleService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单信息 服务实现类
 * </p>
 *
 * @author Jerry
 * @since 2020-11-27
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Autowired
    private SysRoleService sysRoleService;

    @Autowired
    private SysMenuRoleService sysMenuRoleService;

    /**
     * 保存菜单
     *
     * @param sysMenuVo
     * @return {@link Boolean}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean save(SysMenuVO sysMenuVo) {
        SysMenu sysMenu = new SysMenu();
        BeanUtils.copyProperties(sysMenuVo, sysMenu);
        sysMenu.setGmtCreate(new Date());
        sysMenu.setGmtModified(new Date());
        int rows = this.baseMapper.insert(sysMenu);
        if (rows == 0) {
            throw new BaseException(ErrorCodeEnum.SAVE_MENU_FAIL);
        }
        return rows > 0;
    }

    /**
     * 更新菜单
     *
     * @param sysMenuVo
     * @return {@link Boolean}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(SysMenuVO sysMenuVo) {
        SysMenu sysMenu = new SysMenu();
        BeanUtils.copyProperties(sysMenuVo, sysMenu);
        sysMenu.setGmtModified(new Date());
        int rows = this.baseMapper.updateById(sysMenu);
        if (rows == 0) {
            throw new BaseException(ErrorCodeEnum.UPDATE_MENU_FAIL);
        }
        return rows > 0;
    }

    /**
     * 菜单树列表
     *
     * @return {@link List<MenuNode>}
     */
    @Override
    public List<MenuNode> treeList() {
        LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<SysMenu>();
        queryWrapper.eq(SysMenu::getState, Constants.NORMAL);
        queryWrapper.orderByAsc(SysMenu::getParentId, SysMenu::getSort);
        // 获取所有有效菜单
        List<SysMenu> menuList = this.baseMapper.selectList(queryWrapper);
        // 筛选菜单并转换为MenuNode
        List<MenuNode> result = menuList.stream().filter(menu -> menu.getParentId().equals(0L))
                .map(menu -> converterMenu(menu, menuList))
                .collect(Collectors.toList());
        return result;
    }

    /**
     * 获取角色菜单树
     *
     * @param roleId 角色id
     * @return {@link List<Long>}
     */
    @Override
    public List<Long> roleMenutreeList(Long roleId) {
        return this.baseMapper.selectMenuListByRoleId(roleId);
    }

    /**
     * 构建前端菜单选项
     *
     * @param menuNodeList 菜单节点列表
     * @return {@link List<TreeSelect>}
     */
    @Override
    public List<TreeSelect> buildMenuTreeSelect(List<MenuNode> menuNodeList) {
        return menuNodeList.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

    /**
     * 判断是否包含子菜单
     *
     * @param menuId 菜单id
     * @return {@link Boolean}
     */
    @Override
    public Boolean hasChildByMenuId(Long menuId) {
        return this.baseMapper.selectCount(new LambdaQueryWrapper<SysMenu>().
                eq(SysMenu::getParentId, menuId)) > 0;
    }

    /**
     * 判断菜单是否分配给角色
     *
     * @param menuId 菜单id
     * @return {@link Boolean}
     */
    @Override
    public Boolean checkMenuExistRole(Long menuId) {
        return sysMenuRoleService.count(new LambdaQueryWrapper<SysMenuRole>().
                eq(SysMenuRole::getMenuId, menuId)) > 0;
    }

    /**
     * 转换菜单
     *
     * @param menu     菜单
     * @param menuList 菜单列表
     * @return {@link MenuNode}
     */
    private MenuNode converterMenu(SysMenu menu, List<SysMenu> menuList) {
        MenuNode node = new MenuNode();
        BeanUtils.copyProperties(menu, node);

        List<MenuNode> children = menuList.stream()
                .filter(subMenu -> subMenu.getParentId().equals(menu.getId()))
                .map(subMenu -> converterMenu(subMenu, menuList))
                .collect(Collectors.toList());
        node.setChildren(children);
        return node;
    }

    /**
     * 获取用户菜单权限
     *
     * @param userId 用户id
     * @return {@link Set<String>}
     */
    @Override
    public Set<String> getMenuPermsByUserId(Long userId) {
        List<String> perms = this.baseMapper.selectMenuPermsByUserId(userId);
        Set<String> permsSet = new HashSet<>();
        for (String perm : perms) {
            permsSet.addAll(Arrays.asList(perm.trim().split(",")));
        }

        return permsSet;
    }

    /**
     * 获取用户菜单树
     *
     * @param userId 用户id
     * @return {@link List<MenuNode>}
     */
    @Override
    public List<MenuNode> getMenuTreeByUserId(Long userId) {
        List<SysMenu> menuList = new ArrayList<>();
        if (sysRoleService.isAdmin(userId)) {
            menuList.clear();
            menuList.addAll(this.baseMapper.selectMenuTreeAll());
        } else {
            menuList.clear();
            menuList.addAll(this.baseMapper.selectMenuTreeByUserId(userId));
        }

        List<MenuNode> result = menuList.stream().filter(menu -> menu.getParentId().equals(0L))
                .map(menu -> converterMenu(menu, menuList))
                .collect(Collectors.toList());
        return result;
    }

    /**
     * 构建前端菜单路由
     *
     * @param menuNodeList 菜单节点列表
     * @return {@link List<RouterVO>}
     */
    @Override
    public List<RouterVO> buildMenu(List<MenuNode> menuNodeList) {
        List<RouterVO> menuVoList = menuNodeList.stream().map(menu -> {
            RouterVO router = new RouterVO();
            router.setName(StrUtil.upperFirst(menu.getPath()));
            if (0 == menu.getParentId().intValue() && Constants.TYPE_DIR.equals(menu.getMenuType())) {
                router.setPath("/" + menu.getPath());
            } else {
                router.setPath(menu.getPath());
            }
            // 是否可见
            router.setHidden(Constants.UNVISIBLE.equals(menu.getVisible()));
            router.setComponent(getComponent(menu));
            router.setMeta(new MetaVO(menu.getMenuName(), menu.getMenuIcon()));
            List<MenuNode> cMenus = menu.getChildren();
            if (!CollectionUtils.isEmpty(cMenus) && Constants.TYPE_DIR.equals(menu.getMenuType())) {
                router.setRedirect("noRedirect");
                router.setChildren(buildMenu(cMenus));
            }
            return router;
        }).collect(Collectors.toList());
        return menuVoList;
    }

    /**
     * 获取组件信息
     *
     * @param menu 菜单信息
     * @return 组件信息
     */
    public String getComponent(MenuNode menu) {
        String component = Constants.LAYOUT;
        if (StringUtils.isNotEmpty(menu.getComponent())) {
            component = menu.getComponent();
        }
        return component;
    }
}
